/**
 * Going framework : reference to jquery.com
 */
(function(window) {

	function now() {
		return (new Date).getTime();
	}
	var trimReg = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
	var getTypeReg = /(object|[\[\]\s])/g;
	var r20 = /%20/g;
	var rnotwhite = /\S/;
	var jsre = /=\?(&|$)/;
	var rquery = /\?/;
	var rurl = /^(\w+:)?\/\/([^\/?#]+)/;
	var Going = function() {
		// global parameter...
		this.toString = Object.prototype.toString;
		this.ga = 'ga';
		this.gn = 'gn';
		this.gs = 'gs';
		this.support = {
			scriptEval : false
		};
		var id = "script" + now(), root = document.documentElement, script = document.createElement("script");
		script.type = "text/javascript";
		try {
			script.appendChild(document.createTextNode("window." + id + "=1;"));
		} catch (e) {}

		root.insertBefore(script, root.firstChild);

		// Make sure that the execution of code works by injecting a script
		// tag with appendChild/createTextNode
		// (IE doesn't support this, fails, and uses .text instead)
		if (window[id]) {
			this.support.scriptEval = true;
			delete window[id];
		}
		this.paramIterator = {
			iterateObject : function(obj, callback) {
				for (field in obj) {
					if (callback(field, obj[field]) === false) {
						break;
					}
				}
			},
			iterateArray : function(arr, callback) {
				for ( var val = arr[0], i = 0, length = arr.length; i < length && callback(i, val) !== false; val = arr[++i]) {}
			}
		};
		this.paramParser = {
			parseObject : function(obj, add, prevField) {
				$G.paramIterator.iterateObject(obj, function(field, val) {
					$G.param(val, add, prevField ? prevField + '.' + field : field);
				});
			},
			parseArray : function(arr, add, prevField) {
				$G.paramIterator.iterateArray(arr, function(i, val) {
//					var key = prevField ? prevField + '[' + i + ']' : $G.ga + i + ']';
					var key = prevField ? prevField : $G.ga;
					switch ($G.getType(val)) {
					case 'Number':
						add(key, val);
						break;
					case 'String':
						add(key, val);
						break;
					case 'Function':
						add(key, val());
						break;
					default:
						$G.param(val, add, key);
						break;
					}
				});
			},
			parseNumber : function(num, add, prevField) {
				add(prevField ? prevField : this.gn, num);
			},
			parseString : function(str, add, prevField) {
				add(prevField ? prevField : this.gs, str);
			}
		};
		this.ajaxSettings = {
			url : location.href,
			type : "POST",
			contentType : "application/x-www-form-urlencoded",
			async : true,
			XHRequest : window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ? function() {
				return new window.XMLHttpRequest();
			}
					: function() {
						try {
							return new window.ActiveXObject("Microsoft.XMLHTTP");
						} catch (e) {}
					},
			accepts : {
				xml : "application/xml, text/xml",
				html : "text/html",
				script : "text/javascript, application/javascript",
				json : "application/json, text/javascript",
				text : "text/plain",
				_default : "*/*"
			}
		};
	};

	Going.prototype = {
		getType : function(obj) {
			return this.toString.call(obj).replace(getTypeReg, '');
		},
		isFunc : function(obj) {
			return this.getType(obj) === 'Function';
		},
		/**
		 * {a:1,b:2,c:[3,4]} ===> a=1&b=2&c=3&c=4
		 * {a:{d:1,f:5},b:2,c:[3,4]} ===> a.d=1&a.f=5&b=2&c=3&c=4
		 **/
		param : function(obj, callback, prevField) {
			var s = [];
			function add(key, val) {
				// If val is a function, invoke it and return its value
				val = $G.isFunc(val) ? val() : val;
				// s[s.length] = (key) + "=" + (val);
				s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(val);
			}
			// invoke paramParser.parseObject/Array/Number/String
			this.paramParser['parse' + $G.getType(obj)](obj, callback ? callback : add, prevField);
			return s.join("&").replace(r20, "+");
		},
		/**
		 * Reference to jQuery.each(fun),
		 * but "this" can not be used as a value of the array or object in callback function...
		 * Invoke the callback will not change the context, while change the context in jQuery.each...
		 **/
		each : function(obj, callback) {
			if (!obj || !callback)
				return this;
			// invoke paramIterator.iterateObject/Array
			this.paramIterator['iterate' + this.getType(obj)](obj, callback);
		},
		trim : function(text) {
			return (text || "").replace(trimReg, "");
		},
		/**
		 * Extend the object, copy property values from orig,ext to dest
		 **/
		extObj : function(dest, orig, ext) {
			dest = dest ? dest : {};
			for (f in orig) {
				dest[f] = orig[f];
			}
			for (f in ext) {
				dest[f] = ext[f];
			}
			return dest;
		},
		error : function(msg) {
			throw msg;
		},
		/**
		 * Do nothing...
		 **/
		emptyFunc : function() {},
		/**
		 * Parse string to Json object, the string must be like this : {"a":"b","c":["1","3","4"],"d":{"e":9}}
		 **/
		parseJSON : function(data) {
			if (typeof data !== "string" || !data) {
				return null;
			}

			// Make sure leading/trailing whitespace is removed (IE can't handle
			// it)
			data = $G.trim(data);

			// Make sure the incoming data is actual JSON
			// Logic borrowed from http://json.org/json2.js
			if (/^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@").replace(
					/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(
					/(?:^|:|,)(?:\s*\[)+/g, ""))) {

				// Try to use the native JSON parser first
				return window.JSON && window.JSON.parse ? window.JSON.parse(data) : (new Function("return " + data))();

			} else {
				$G.error("Invalid JSON: " + data);
			}
		},

		// Evalulates a script in a global context
		globalEval : function(data) {
			if (data && rnotwhite.test(data)) {
				// Inspired by code by Andrea Giammarchi
				// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
				var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document
						.createElement("script");

				script.type = "text/javascript";

				if ($G.support.scriptEval) {
					script.appendChild(document.createTextNode(data));
				} else {
					script.text = data;
				}

				// Use insertBefore instead of appendChild to circumvent an IE6
				// bug.
				// This arises when a base node is used (#2709).
				head.insertBefore(script, head.firstChild);
				head.removeChild(script);
			}
		},
		// Determines if an XMLHttpRequest was successful or not
		httpSuccess : function(xhr) {
			try {
				// IE error sometimes returns 1223 when it should be 204 so
				// treat it as success, see #1450
				return !xhr.status && location.protocol === "file:" ||
				// Opera returns 0 when status is 304
				(xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || xhr.status === 1223
						|| xhr.status === 0;
			} catch (e) {}
			return false;
		},
		// Determines if an XMLHttpRequest returns NotModified
		httpNotModified : function(xhr, url) {
			var lastModified = xhr.getResponseHeader("Last-Modified"), etag = xhr.getResponseHeader("Etag");

			if (lastModified) {
				$G.lastModified[url] = lastModified;
			}

			if (etag) {
				$G.etag[url] = etag;
			}

			// Opera returns 0 when status is 304
			return xhr.status === 304 || xhr.status === 0;
		},

		httpData : function(xhr, type, s) {
			var contentType = xhr.getResponseHeader("content-type") || "", xml = type === "xml" || !type
					&& contentType.indexOf("xml") >= 0, data = xml ? xhr.responseXML : xhr.responseText;

			if (xml && data.documentElement.nodeName === "parsererror") {
				$G.error("parsererror");
			}

			// Allow a pre-filtering function to sanitize the response
			// s is checked to keep backwards compatibility
			if (s && s.dataFilter) {
				data = s.dataFilter(data, type);
			}

			// The filter can actually parse the response
			if (typeof data === "string") {
				// Get the JavaScript object, if JSON is used.
				if (type === "json" || !type && contentType.indexOf("json") >= 0) {
					data = $G.parseJSON(data);

					// If the type is "script", eval it in global context
				} else if (type === "script" || !type && contentType.indexOf("javascript") >= 0) {
					$G.globalEval(data);
				}
			}

			return data;
		},
		lastModified : {},
		etag : {},
		ajax : function(origObj) {
			// initial
			var s = this.extObj( {}, this.ajaxSettings, origObj), type;
			if (s.data && typeof s.data !== "string") {
				s.data = this.param(s.data);
			}
			var jsonp, status, data, type = s.type.toUpperCase();

			// Handle JSONP Parameter Callbacks
			if (s.dataType === "jsonp") {
				if (type === "GET") {
					if (!jsre.test(s.url)) {
						s.url += (rquery.test(s.url) ? "&" : "?") + (s.jsonp || "callback") + "=?";
					}
				} else if (!s.data || !jsre.test(s.data)) {
					s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
				}
				s.dataType = "json";
			}

			// Build temporary JSONP function
			if (s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url))) {
				jsonp = s.jsonpCallback || ("jsonp" + jsc++);

				// Replace the =? sequence both in the query string and the data
				if (s.data) {
					s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
				}

				s.url = s.url.replace(jsre, "=" + jsonp + "$1");

				// We need to make sure
				// that a JSONP style response is executed properly
				s.dataType = "script";

				// Handle JSONP-style loading
				window[jsonp] = window[jsonp] || function(tmp) {
					data = tmp;
					success();
					complete();
					// Garbage collect
					window[jsonp] = undefined;

					try {
						delete window[jsonp];
					} catch (e) {}

					if (head) {
						head.removeChild(script);
					}
				};
			}

			if (s.dataType === "script" && s.cache === null) {
				s.cache = false;
			}

			if (s.cache === false && type === "GET") {
				var ts = now();

				// try replacing _= if it is there
				var ret = s.url.replace(rts, "$1_=" + ts + "$2");

				// if nothing was replaced, add timestamp to the end
				s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
			}

			// If data is available, append data to url for get requests
			if (s.data && type === "GET") {
				s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
			}

			// Matches an absolute URL, and saves the domain
			var parts = rurl.exec(s.url), remote = parts
					&& (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);

			// If we're requesting a remote document
			// and trying to load JSON or Script with a GET
			if (s.dataType === "script" && type === "GET" && remote) {
				var head = document.getElementsByTagName("head")[0] || document.documentElement;
				var script = document.createElement("script");
				script.src = s.url;
				if (s.scriptCharset) {
					script.charset = s.scriptCharset;
				}

				// Handle Script loading
				if (!jsonp) {
					var done = false;

					// Attach handlers for all browsers
					script.onload = script.onreadystatechange = function() {
						if (!done
								&& (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
							done = true;
							success();
							complete();

							// Handle memory leak in IE
							script.onload = script.onreadystatechange = null;
							if (head && script.parentNode) {
								head.removeChild(script);
							}
						}
					};
				}

				// Use insertBefore instead of appendChild to circumvent an IE6
				// bug.
				// This arises when a base node is used (#2709 and #4378).
				head.insertBefore(script, head.firstChild);

				// We handle everything using the script element injection
				return undefined;
			}
			var requestDone = false;
			var xhr = s.XHRequest();
			if (s.username) {
				xhr.open(type, s.url, s.async, s.username, s.password);
			} else {
				xhr.open(type, s.url, s.async);
			}
			try {
				if (s.data || origObj && origObj.contentType) {
					xhr.setRequestHeader("Content-Type", s.contentType);
				}
				// Set the If-Modified-Since and/or If-None-Match header, if in
				// ifModified mode.
				if (s.ifModified) {
					if (jQuery.lastModified[s.url]) {
						xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
					}

					if (jQuery.etag[s.url]) {
						xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
					}
				}
				// Set header so the called script knows that it's an
				// XMLHttpRequest
				xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
				xhr.setRequestHeader("Accept", s.dataType && s.accepts[s.dataType] ? s.accepts[s.dataType] + ", */*"
						: s.accepts._default);
			} catch (e) {}
			if (s.beforeSend && s.beforeSend(xhr, s) === false) {
				// close opended socket
				xhr.abort();
				return false;
			}
			var onreadystatechange = xhr.onreadystatechange = function(isTimeout) {
				// The request was aborted
				if (!xhr || xhr.readyState === 0 || isTimeout === "abort") {
					// Opera doesn't call onreadystatechange before this point
					// so we simulate the call
					if (!requestDone) {
						complete();
					}

					requestDone = true;
					if (xhr) {
						xhr.onreadystatechange = $G.emptyFunc;
					}

					// The transfer is complete and the data is available, or
					// the request timed out
				} else if (!requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout")) {
					requestDone = true;
					xhr.onreadystatechange = $G.emptyFunc;

					status = isTimeout === "timeout" ? "timeout" : !$G.httpSuccess(xhr) ? "error" : s.ifModified
							&& $G.httpNotModified(xhr, s.url) ? "notmodified" : "success";

					var errMsg;

					if (status === "success") {
						// Watch for, and catch, XML document parse errors
						try {
							// process the data (runs the xml through httpData
							// regardless of callback)
							data = $G.httpData(xhr, s.dataType, s);
						} catch (err) {
							status = "parsererror";
							errMsg = err;
						}
					}

					// Make sure that the request was successful or notmodified
					if (status === "success" || status === "notmodified") {
						// JSONP handles its own success callback
						if (!jsonp) {
							success();
						}
					} else {
						$G.handleError(s, xhr, status, errMsg);
					}

					// Fire the complete handlers
					complete();

					if (isTimeout === "timeout") {
						xhr.abort();
					}

					// Stop memory leaks
					if (s.async) {
						xhr = null;
					}
				}
			};
			// Override the abort handler, if we can (IE doesn't allow it, but
			// that's OK)
			// Opera doesn't fire onreadystatechange at all on abort
			try {
				var oldAbort = xhr.abort;
				xhr.abort = function() {
					if (xhr) {
						oldAbort.call(xhr);
					}

					onreadystatechange("abort");
				};
			} catch (e) {}

			// Timeout checker
			if (s.async && s.timeout > 0) {
				setTimeout(function() {
					// Check to see if the request is still happening
					if (xhr && !requestDone) {
						onreadystatechange("timeout");
					}
				}, s.timeout);
			}

			// Send the data
			try {
				xhr.send(type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null);
			} catch (e) {
				$G.handleError(s, xhr, null, e);
				// Fire the complete handlers
				complete();
			}

			// firefox 1.5 doesn't fire statechange for sync requests
			if (!s.async) {
				onreadystatechange();
			}

			function success() {
				// If a local callback was specified, fire it and pass it the
				// data
				if (s.success) {
					s.success(data, status, xhr);
				}
			}

			function complete() {
				// Process result
				if (s.complete) {
					s.complete(xhr, status);
				}
			}

			// return XMLHttpRequest to allow aborting the request etc.
			return xhr;
		},
		handleError : function(s, xhr, status, e) {
			// If a local callback was specified, fire it
			if (s.error) {
				s.error(xhr, status, e);
			}
		}
	};

	var $G = new Going();

	window.$G = $G;

})(window);